package com.sheep.jucdemo.part6.part6_5;

import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.locks.StampedLock;

/**
 * @Date 2022/4/25 23:02
 * @Created by sheep
 */
public class Test6_5_2_1 {
    class Point {
        // 成员变量
        private double x, y;
        // 锁实例
        private final StampedLock sl = new StampedLock();




        // 排它锁-写锁（writeLock）
        void move(double deltaX, double deltaY) { // an exclusively locked method

            ConcurrentLinkedQueue<Object> objects = new ConcurrentLinkedQueue<>();
            long stamp = sl.writeLock();
            try {
                x += deltaX;
                y += deltaY;
            } finally {
                sl.unlockWrite(stamp);
            }
        }

        // 乐观锁（tyrOptimisticRead）
        double distanceFromOrigin() { // A read-only method
            // （1）尝试获取乐观读锁
            long stamp = sl.tryOptimisticRead();
            // （2）将全部变量复制到方法体栈内
            double currentX = x, currentY = y;
            // （3）检查再（1）处获取了读锁戳记后，锁有没有被其他写线程排它性抢占
            if (!sl.validate(stamp)) {
                // （4）如果被抢占则获取一个共享读锁（悲观锁）
                stamp = sl.readLock();
                try {
                    // （5）将全部变量复制到方法体栈内
                    currentX = x;
                    currentY = y;
                } finally {
                    // 释放共享读锁
                    sl.unlockRead(stamp);
                }
            }
            // （7）返回计算结果
            return Math.sqrt(currentX * currentX + currentY * currentY);
        }

        // 使用悲观锁获取读锁，并尝试转换为写锁
        void moveIfAtOrigin(double newX, double newY) { // upgrade
            // Could instead start with optimistic, not read mode
            // （1）这里可以使用乐观读锁替换
            long stamp = sl.readLock();
            try {
                // （2）如果当前点再原点则移动
                while (x == 0.0 && y == 0.0) {
                    // （3）尝试将获取的读锁升级为写锁
                    long ws = sl.tryConvertToWriteLock(stamp);
                    // （4）升级成功，则更新戳记，并设置坐标值，然后退出循环
                    if (ws != 0L) {
                        stamp = ws;
                        x = newX;
                        y = newY;
                        break;
                    }
                    else {
                        // 读锁升级写锁失败则释放读锁，显示获取独占写锁，然后循环重试
                        sl.unlockRead(stamp);
                        stamp = sl.writeLock();
                    }
                }
            } finally {
                // 释放锁
                sl.unlock(stamp);
            }
        }
    }
}
